package muhnes.sizoo.controlador;

import java.io.IOException;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.ComponentSystemEvent;
import muhnes.sizoo.dal.IDataAccessObject;
import muhnes.sizoo.util.Transaccion;

/***
 * Clase base para realizar todas las operaciones de las tablas crud
 * 
 */
@ViewScoped
public abstract class CRUDBean< E, DAO extends IDataAccessObject > 
    implements Serializable {
    
    @ManagedProperty(value="#{sesionBean}")
    protected SesionBean sesionBean;
    
    private Class<?> claseEntidad;
    protected E entidad;
    protected List<E> listaEntidades;
    protected List<E> listaEntidadesFiltradas;
    protected Transaccion operacionActual;
    protected String vistaProtegida;

    public CRUDBean( Class<?> claseEntidad ) {
        this.claseEntidad = claseEntidad;
        entidad = nuevaInstancia();
        operacionActual = Transaccion.NINGUNA;
    }
    
    protected abstract DAO getDAO();
    
    protected abstract boolean debeValidar( Transaccion transaccion );
    
    public void setSesionBean(SesionBean sesionBean) {
        this.sesionBean = sesionBean;
    }
    
    public E getEntidad() {
        return entidad;
    } 
    
    public void setEntidad( E entidad ) {
        this.entidad = entidad;
    }
    
    public List<E> getListaEntidades() {
        if( listaEntidades == null ) {
            DAO dao = getDAO();
            listaEntidades = (List<E>)dao.buscarTodos();
        }
        return listaEntidades;
    }
    
    public List<E> getListaEntidadesFiltradas() {
        return listaEntidadesFiltradas;
    }
    
    public void setListaEntidadesFiltradas( List<E> listaEntidadesFiltradas ) {
        this.listaEntidadesFiltradas = listaEntidadesFiltradas;
    }
    
    public Transaccion getOperacionActual() {
        return operacionActual;
    }

    public void setOperacionActual( Transaccion operacionActual ) {
        this.operacionActual = operacionActual;
    }
    
    private E nuevaInstancia() {
        E instancia = null;
                
        try {
            instancia = (E)claseEntidad.newInstance();
        } catch (InstantiationException ex) {
            Logger.getLogger( CRUDBean.class.getName() )
                    .log( Level.SEVERE, null, ex );
        } catch (IllegalAccessException ex) {
            Logger.getLogger( CRUDBean.class.getName() )
                    .log( Level.SEVERE, null, ex );
        }
        finally {
            return instancia;
        }
    }
    
    public void validarAcceso(ComponentSystemEvent evento) throws IOException {
        boolean accesoPermitido = sesionBean.verificarPermiso("consultar", vistaProtegida); 
        
        if( !accesoPermitido ) {
            FacesContext contexto = FacesContext.getCurrentInstance();
            String nombreContexto = contexto.getExternalContext().getRequestContextPath();
            String urlError = nombreContexto + "/paginas/errores/error.xhtml";
            contexto.getExternalContext().redirect(urlError);
        }
    }

    public boolean tieneErrores( String componente ) {
        FacesContext contexto = FacesContext.getCurrentInstance();
        Iterator<String> componentes = contexto.getClientIdsWithMessages();
        while ( componentes.hasNext() ) {
            if( componente.equals( componentes.next() ) ) {
                return true;
            }
        }
        
        return false;
    }

    public void prepararAdicion( ActionEvent event  ) {
        entidad = nuevaInstancia();
        operacionActual = Transaccion.INSERCION;
    }
    
    public void prepararModificacion( E entidad ) {
        this.entidad = entidad;
        operacionActual = Transaccion.ACTUALIZACION;
    }
    
    public void prepararEliminacion( E entidad ) {
        this.entidad = entidad;
        operacionActual = Transaccion.ELIMINACION;
    }
    
    protected boolean validarUnicidad() {
        DAO dao = getDAO();
        if( debeValidar( operacionActual ) && 
            dao.verificarUnicidad( entidad ) ) {
            FacesContext contexto = FacesContext.getCurrentInstance();
            contexto.addMessage(null, new FacesMessage(
                    FacesMessage.SEVERITY_ERROR,
                    "Ya existe este registro", "Registro duplicado")
            );
            contexto.validationFailed();
            return false;
        } 
        else {
            return true;
        }
    }
    
    public void guardarEntidad( ActionEvent event ) {
        if( !validarUnicidad() ) {
            return;
        }
        
        if( operacionActual == Transaccion.INSERCION ) {
            agregarEntidad( event );
        }
        else if( operacionActual == Transaccion.ACTUALIZACION ) {
            actualizarEntidad( event );
        }
        else {
            operacionActual = Transaccion.NINGUNA;
        }
    }
    
    public void agregarEntidad( ActionEvent event ) {
        DAO dao = getDAO();
        dao.guardar( entidad );
        recrearModelo();
    }
    
    public void actualizarEntidad( ActionEvent event ) {
        DAO dao = getDAO();        
        dao.actualizar( entidad );
        recrearModelo();
    }
    
    protected boolean validarDependencia() {
        DAO dao = getDAO();
        if( debeValidar( operacionActual ) && 
            dao.verificarDependencia( entidad ) ) {
            FacesContext contexto = FacesContext.getCurrentInstance();
            contexto.addMessage(null, new FacesMessage(
                    FacesMessage.SEVERITY_ERROR,
                    "Existen registros asociados", "Registro padre")
            );
            contexto.validationFailed();
            return false;
        } 
        else {
            return true;
        }
    }
    
    public void eliminarEntidad( ActionEvent event ) {
        if( !validarDependencia() ) {
            return;
        } 
        
        DAO dao = getDAO();
        dao.eliminar( entidad );
        recrearModelo();
    }
    
    public void revertirCambios( ActionEvent event ) {
        if( operacionActual == Transaccion.ACTUALIZACION ) {
            DAO dao = getDAO();
            dao.revertirCambios( entidad );
        }
        
        operacionActual = Transaccion.NINGUNA;
    }
   
    void recrearModelo() {
        listaEntidades = null;
        listaEntidadesFiltradas = null;
        operacionActual = Transaccion.NINGUNA;
        entidad = nuevaInstancia();
    }

}


